package com.googlecode.flex_solder.controls  {

  /**
   * $Id: DateCombo.as 16 2009-01-03 03:38:29Z fuller.sethm $
   *
   * Author: Seth M. Fuller
   * Copyright: 2008
   *
   *  This class, like the TimeCombo class was inspired by the
   *  TimeInput class in the Yahoo Astra Flex components
   *  library. There is no equivalent to the DateCombo class in the
   *  Astra library, since Flex already provides a DateField and
   *  DateChooser control.
   *
   *  This class, like the TimeCombo class can return a null Date
   *  object if the user does not select a full date (month,
   *  day-of-month, and year) in all three ComboBox controls. When you
   *  retrieve the dateValue property (a Date object) the DateCombo
   *  class calculates the date and also sets the month, day, and year
   *  properties. If the user hasn't selected a full date, then the
   *  dateValue returned is null and the month, day, and year
   *  properties are set to -1.
   *
   *  Permission is hereby granted, free of charge, to any person
   *  obtaining a copy of this software and associated documentation
   *  files (the "Software"), to deal in the Software without
   *  restriction, including without limitation the rights to use,
   *  copy, modify, merge, publish, distribute, sublicense, and/or
   *  sell copies of the Software, and to permit persons to whom the
   *  Software is furnished to do so, subject to the following
   *  conditions:
   *
   *  The above copyright notice and this permission notice shall be
   *  included in all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
   *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   *  OTHER DEALINGS IN THE SOFTWARE.
   *
   * This file is licensed under the BSD (revised) open source
   * license.
   *
   * This is part of the flex-solder components hosted at
   * flex-solder.googlecode.com.
   *
   *======================================================================
   *
   * Contains third-party source code released with the same license terms
   * that appear above with the following copyright notice:
   *
   * Copyright (c) 2008 Yahoo! Inc.  All rights reserved.  The copyrights
   * embodied in the content of this file are licensed under the BSD
   * (revised) open source license
   *
   */

  import com.googlecode.flex_solder.controls.BaseMultiCombo;

  import flash.events.Event;
  import flash.events.FocusEvent;
  import flash.events.KeyboardEvent;
  import flash.ui.Keyboard;
  import flash.text.TextLineMetrics;
	
  import mx.collections.ArrayCollection;

  import mx.core.EdgeMetrics;
  import mx.core.IBorder;
  import mx.core.UITextField;
  import mx.containers.HBox;
  import mx.controls.Text;
  import mx.controls.ComboBox;

  import mx.events.ListEvent;
  import mx.events.FlexEvent;

  import mx.styles.CSSStyleDeclaration;
  import mx.styles.StyleManager;
	
  //--------------------------------------
  //  Events
  //--------------------------------------
	
  /**
   *  Dispatched when text in the DateCombo control changes
   *  through user input.
   *  This event does not occur if you use data binding or 
   *  ActionScript code to change the text.
   *
   *  <p>Even though the default value of the <code>Event.bubbles</code> property 
   *  is <code>true</code>, this control dispatches the event with 
   *  the <code>Event.bubbles</code> property set to <code>false</code>.</p>
   *
   *  @eventType flash.events.Event.CHANGE
   */
  [Event(name="change", type="flash.events.Event")]
		
    //--------------------------------------
    //  Styles
    //--------------------------------------

    /**
     * If true, the years value will be displayed.
     * 
     * @default true
     */
    [Style(name="showYears", type="Boolean", inherit="no")]

    /**
     * The String displayed for the months toolTip.
     * 
     * @default Months
     */
    [Style(name="monthsText", type="String", inherit="no")]

    /**
     * The String displayed for the days toolTip.
     * 
     * @default Days
     */
    [Style(name="daysText", type="String", inherit="no")]

    /**
     * The String displayed for the years toolTip.
     * 
     * @default Years
     */
    [Style(name="yearsText", type="String", inherit="no")]

    /**
     * Set to the separator to use between the parts of the date
     * 
     * @default '/'
     */
    [Style(name="dateSeparator", type="String", inherit="no")]

    /**
     * The order that the date parts are display in.
     * 
     * @default 'MDY'
     */
    [Style(name="dateDisplayOrder", type="String", inherit="no")]

    //--------------------------------------
    //  Other Metadata
    //--------------------------------------
	
    [DefaultBindingProperty(source="value", destination="value")]
	
    /**
     * An multi-comboBox control for date values. 
     * Includes comboBoxes for months, days, and years.
     * 
     * @author Seth M. Fuller
     */
    public class DateCombo extends BaseMultiCombo {

    /**
     * DATE_SEPARATOR_SLASH - Used with the dateSeparator style. This
     * is the default date separator.
     */
    public static const DATE_SEPARATOR_SLASH:String = "/";
    /**
     * DATE_SEPARATOR_DASH - Used with the dateSeparator style.
     */
    public static const DATE_SEPARATOR_DASH:String = "-";

    /**
     * MONTH_DAY_YEAR - Used with the dateDisplayOrder style. This is
     * the default display order.
     */
    public static const MONTH_DAY_YEAR:String = "MDY";
    /**
     * DAY_MONTH_YEAR - Used with the dateDisplayOrder style.
     */
    public static const DAY_MONTH_YEAR:String = "DMY";
    /**
     * YEAR_DAY_MONTH - Used with the dateDisplayOrder style.
     */
    public static const YEAR_DAY_MONTH:String = "YDM";
    /**
     * YEAR_MONTH_DAY - Used with the dateDisplayOrder style.
     */
    public static const YEAR_MONTH_DAY:String = "YMD";

    private var layoutStyleChanged:Boolean = false;

    private static var monthsPerYear:int = 12;
    private static var maxDaysPerMonth:int = 31;

    /**
     * Controls how many years are shown in the yearsCombo
     * dropdown. The default is 110 years.
     */
    private var _yearsToList:int = 110;
    private var _firstYear:int = 0;
    private var _lastYear:int = 0;

    protected var monthsCombo:ComboBox = null;
    protected var daysCombo:ComboBox = null;
    protected var yearsCombo:ComboBox = null;

    protected var monthsList:ArrayCollection = null;
    protected var daysList:ArrayCollection = null;
    protected var yearsList:ArrayCollection = null;
		
    public var componentWidths:ArrayCollection = null;

    public static const TEXT_WIDTH_PADDING:Number = 5;
    private var dateComboWidth:Number = 0;
    private var dateComboHeight:Number = 0;
    private const minComboWidth:Number = 40;

    public static const dateComboWidthPad:Number = 6;

    protected static var monthsInc:int = 1;
    protected static var daysInc:int = 1;
    protected static var yearsInc:int = 1;

    //--------------------------------------
    //  Static Methods
    //--------------------------------------
	
    /**
     * @private
     * Sets the default style values for this control.
     */
    public static function initializeStyles():void
    {
      var styleDeclaration:CSSStyleDeclaration
	= StyleManager.getStyleDeclaration("DateCombo");
      if(!styleDeclaration)
	{
	  styleDeclaration = new CSSStyleDeclaration();
	}
			
      styleDeclaration.defaultFactory = function():void {
	this.showYears = true;
	this.dateSeparator = DateCombo.DATE_SEPARATOR_SLASH;
	this.dateDisplayOrder = DateCombo.MONTH_DAY_YEAR;
	this.monthsText = "Months";
	this.daysText = "Days";
	this.yearsText = "Years";
      };
			
      StyleManager.setStyleDeclaration("DateCombo", styleDeclaration, false);
    }
    initializeStyles();
		
		
    //--------------------------------------
    //  Constructor
    //--------------------------------------
	
    /**
     * Constructor.
     */
    public function DateCombo()
    {
      super();
      this.fieldCount = 4;

    }
		
    //--------------------------------------
    //  Properties
    //--------------------------------------

    /**
     * @private
     * Storage for the dateValue property.
     */
    protected var _dateValue:Date = null;
		
    private var _currDate:Date = new Date();

    private var _month:Number = -1;
    private var _day:Number = -1;
    private var _year:Number = 0;
		
    /**
     * @private
     * The UITextFields for the date separators.
     */
    protected var separators:Array = [];
		
    /**
     * @private
     * The width of a two-digit number. Used in measurement calculations and
     * positioning.
     */
    private var _numberWidth:Number = 0;
    private var _numberWidthMT:Number = 0;

    private var _numberHeight:Number = 0;
    private var _numberHeightMT:Number = 0;
		
    //--------------------------------------
    //  Public Methods
    //--------------------------------------
		
    /**
     * @private
     */
    override public function styleChanged(styleProp:String):void {
      super.styleChanged(styleProp);
			
      if(!styleProp || styleProp.indexOf("font") >= 0 
	 || styleProp.indexOf("text") >= 0
	|| StyleManager.isSizeInvalidatingStyle(styleProp)) {
	layoutStyleChanged = true;
				
// 	trace('DateCombo.styleChanged: ' + styleProp);
	initWidthValues(true);

	this.invalidateSize();
	this.invalidateDisplayList();
      }
    }
		
    //--------------------------------------
    //  Protected Methods
    //--------------------------------------

		
    protected function initWidthValues(forceRecalc:Boolean = false):void {
      if (_numberWidth > 0 && !forceRecalc) {
// 	trace('Not running initWidthValues');
 	return;
      }

      //we have to do this even if we have explicit dimensions
      //because positioning relies on the size of two characters
      var doubleZero:String = "00";
      var tempTextField:UITextField = new UITextField();
      tempTextField.styleName = this.styleName;
      tempTextField.ignorePadding = true;
      tempTextField.text = doubleZero;
      
      //figured invalidation and validation would be enough
      //but we have to add and remove the UITextField too!
      this.addChild(tempTextField);
				
      _numberWidth = tempTextField.measuredWidth;
      _numberHeight = tempTextField.measuredHeight;
      var doubleZeroMT:TextLineMetrics = measureText(doubleZero);
      _numberWidthMT = doubleZeroMT.width + TEXT_WIDTH_PADDING;
      _numberHeightMT = doubleZeroMT.height;

      this.removeChild(tempTextField);

      this.invalidateSize();
      this.invalidateDisplayList();
//       trace('DateCombo.initWidthValues()[' + this.name + ']');

    }

    /**
     * @private
     */
    override protected function createChildren():void {
      super.createChildren();
      var showYears:Boolean = this.getStyle("showYears");
			
      var separatorField:UITextField = getSeparator();
      this.addChild(separatorField);
      this.separators.push(separatorField);
      if (showYears) {
	separatorField = getSeparator();
	this.addChild(separatorField);
	this.separators.push(separatorField);
      }
			
      // update the input fields
      monthsCombo = createValueComboBox();
      monthsCombo.dataProvider = getMonthsList();
      monthsCombo.toolTip = this.getStyle("monthsText");
      monthsCombo.restrict = "0-9";
      monthsCombo.addEventListener(ListEvent.CHANGE, setDoMFilter);
      monthsCombo.labelFunction = getNumberValue;
			
      daysCombo = createValueComboBox();
      daysCombo.dataProvider = getDaysList();
      daysCombo.toolTip = this.getStyle("daysText");
      daysCombo.restrict = "0-9";
      daysCombo.labelFunction = getNumberValue;
			
      if (showYears) {
	yearsCombo = createValueComboBox();
	yearsCombo.dataProvider = getYearsList();
	yearsCombo.toolTip = this.getStyle("yearsText");
	yearsCombo.restrict = "0-9";
	yearsCombo.labelFunction = getNumberValue;
	yearsCombo.addEventListener(ListEvent.CHANGE, setDoMFilter);
      }
			
//       trace('DateCombo.createChildren()[' + this.name + ']');

    }
		
    protected function createValueComboBox():ComboBox {
      var comboBox:ComboBox = createComboBox();

      return comboBox;

    }

    protected function getSeparator():UITextField {
      var dateSeparator:String = getStyle("dateSeparator");
      var separatorField:UITextField = new UITextField();
      separatorField.styleName = this;
      separatorField.ignorePadding = true;
      separatorField.selectable = false;
      separatorField.enabled = this.enabled;
      separatorField.text = dateSeparator;

      return separatorField;
    }


    /**
     * @private
     */
    override protected function commitProperties():void
    {
      if(this.enabledChanged)
	{
	  for each(var separator:UITextField in this.separators)
		    {
		      separator.enabled = this.enabled;
		    } 
	}
			
      super.commitProperties();
			
      var showYears:Boolean = this.getStyle("showYears");
			
      setComboValues();


    }
		
    /**
     * @private
     */
    override protected function measure():void {
      super.measure();
      if (dateComboWidth && !layoutStyleChanged) {
// 	trace('DateCombo.already measured');
	measuredMinWidth = dateComboWidth;
	measuredMinHeight = dateComboHeight;
	measuredWidth = dateComboWidth;
	measuredHeight = dateComboHeight;
	return;
      }

      layoutStyleChanged = false;
      initWidthValues();

      var showYears:Boolean = this.getStyle("showYears");
			
      if (this.border) {
	var metrics:EdgeMetrics = (this.border is IBorder 
				   ? IBorder(this.border).borderMetrics
				   : EdgeMetrics.EMPTY);
	this.measuredWidth = metrics.left + metrics.right;
	this.measuredHeight = metrics.top + metrics.bottom;
      }
			
      var compWidths:Number = getComponentWidths();
      if (compWidths > 0) {
	this.measuredWidth += compWidths;
      }
       this.measuredWidth += dateComboWidthPad;

       this.measuredHeight += getComboHeight();
       this.measuredHeight += comboHeightPad;

      // Since we don't want the text being cut off, the min width and
      // height are the same as the regular values.
      this.measuredMinWidth = this.measuredWidth;
      this.measuredMinHeight = this.measuredHeight;
      
      dateComboWidth = measuredWidth;
      dateComboHeight = measuredHeight;
    }

    /**
     * @private
     * Overrides default layout algorithm to include the separator fields. 
     */
    override protected function layoutChildren():void {
      var dpWidth:Number;
      var datePartCombo:ComboBox;
      var showYears:Boolean = this.getStyle("showYears");

      var metrics:EdgeMetrics = (this.border && this.border is IBorder
				 ? IBorder(this.border).borderMetrics
				 : EdgeMetrics.EMPTY);

      var xPosition:Number = metrics.left;
      var yPosition:Number = metrics.top;
      if (!showYears) {
	comboPercentWidth = 45;
      }
			
      datePartCombo = getDatePartCombo(1);
      if (null != datePartCombo) {
	datePartCombo.x = xPosition;
	datePartCombo.y = yPosition;
	dpWidth = getDatePartComboWidth(1);
	datePartCombo.minWidth = dpWidth;
	datePartCombo.minHeight = getComboHeight();
	datePartCombo.setActualSize(dpWidth, getComboHeight());

	xPosition += datePartCombo.width;
      }
			
      var separatorField:UITextField = getDatePartSeparator(0);
      if (null != separatorField) {
	separatorField.x = xPosition;
	separatorField.y = yPosition;
	var sepWidth:Number = Math.min(Math.max(0, 
						unscaledWidth - metrics.right
						- separatorField.x), 
				       separatorField.measuredWidth);

	separatorField.setActualSize(sepWidth, getComboHeight());
	xPosition += separatorField.measuredWidth;
      }			

      datePartCombo = getDatePartCombo(2);
      if (null != datePartCombo) {
	datePartCombo.x = xPosition;
	datePartCombo.y = yPosition;
	dpWidth = getDatePartComboWidth(2);
	datePartCombo.minWidth = dpWidth;
	datePartCombo.minHeight = getComboHeight();
	datePartCombo.setActualSize(dpWidth, getComboHeight());

	xPosition += datePartCombo.width;
      }

      separatorField = getDatePartSeparator(1);
      if (null != separatorField) {
	sepWidth = Math.min(Math.max(0, unscaledWidth - metrics.right
				     - separatorField.x), 
			    separatorField.measuredWidth);
	separatorField.x = xPosition;
	separatorField.y = yPosition;

	separatorField.setActualSize(sepWidth, getComboHeight());
	xPosition += separatorField.measuredWidth;
      }
			
      datePartCombo = getDatePartCombo(3);
      if (null != datePartCombo) {
	datePartCombo.x = xPosition;
	datePartCombo.y = yPosition;
	dpWidth = getDatePartComboWidth(3); 
 	datePartCombo.setActualSize(dpWidth, getComboHeight());
	datePartCombo.minWidth = dpWidth;
	datePartCombo.minHeight = getComboHeight();

	xPosition += datePartCombo.width;
      }


    }

    protected function getDatePartCombo(datePartIdx:int):ComboBox {
      var datePartCombo:ComboBox = null;
      var dateDisplayOrder:String = getStyle("dateDisplayOrder");

      if (datePartIdx == 1) {
	if (dateDisplayOrder == DateCombo.MONTH_DAY_YEAR) {
	  datePartCombo = monthsCombo;
	}
	if (dateDisplayOrder == DateCombo.DAY_MONTH_YEAR) {
	  datePartCombo = daysCombo;
	}
	if (dateDisplayOrder == DateCombo.YEAR_DAY_MONTH
	    || dateDisplayOrder == DateCombo.YEAR_MONTH_DAY) {
	  datePartCombo = yearsCombo;
	}
      }
      if (datePartIdx == 2) {
	if (dateDisplayOrder == DateCombo.MONTH_DAY_YEAR
	    || dateDisplayOrder == DateCombo.YEAR_DAY_MONTH) {
	  datePartCombo = daysCombo;
	}
	if (dateDisplayOrder == DateCombo.DAY_MONTH_YEAR
	    || dateDisplayOrder == DateCombo.YEAR_MONTH_DAY) {
	  datePartCombo = monthsCombo;
	}
      }
      if (datePartIdx == 3) {
	if (dateDisplayOrder == DateCombo.MONTH_DAY_YEAR
	    || dateDisplayOrder == DateCombo.DAY_MONTH_YEAR) {
	  datePartCombo = yearsCombo;
	}
	if (dateDisplayOrder == DateCombo.YEAR_DAY_MONTH) {
	  datePartCombo = monthsCombo;
	}
	if (dateDisplayOrder == DateCombo.YEAR_MONTH_DAY) {
	  datePartCombo = daysCombo;
	}
      }

      return datePartCombo;

    }

    protected function getDatePartSeparator(separatorIdx:int):UITextField {
      var dateSeparator:String = getStyle("dateSeparator");
      var separatorField:UITextField = UITextField(this.separators[separatorIdx]);
      if (null != separatorField) {
	separatorField.text = dateSeparator;
      }

      return separatorField;
    }

    protected function getComponentWidths():Number {
      var ttlWidth:Number = 0;

      var showYears:Boolean = this.getStyle("showYears");

      var compWidth:Number = 0;
			
      compWidth = getComboWidth(); // monthsCombo.width;
      ttlWidth += compWidth;


      var separatorField:UITextField = UITextField(this.separators[0]);
      compWidth = separatorField.width;
      ttlWidth += compWidth;
			
      compWidth = getComboWidth(); // daysCombo.width;
      ttlWidth += compWidth;

      separatorField = UITextField(this.separators[1]);
      if (null != separatorField) {
	compWidth = separatorField.width;

	ttlWidth += compWidth;
      }
			
      if (showYears) {

	compWidth = getComboWidth(2); // yearsCombo.width;

	ttlWidth += compWidth;
      }

      ttlWidth += dateComboWidthPad;

//       trace('[' + this.name + '] idx=' + idx + ' ttlWidth=' + ttlWidth);

      return ttlWidth;
    }
	
    protected function setDoMFilter(event:ListEvent):void {

	daysList.filterFunction = daysInMonthFilter;
	daysList.refresh();

    }

    protected function daysInMonthFilter(item:Object):Boolean {
      var monthHasDay:Boolean = true;
      var dateMonth:Number;
      var dateYear:Number;
      var dayOfMonth:Number = item as Number;

      if (dayOfMonth <= 28) {
	return monthHasDay;
      }

      if (null != monthsCombo && monthsCombo.selectedIndex >= 0) {
	dateMonth = monthsCombo.selectedItem as Number;
      }
      if (null != yearsCombo && yearsCombo.selectedIndex >= 0) {
	dateYear = yearsCombo.selectedItem as Number;
      }
      if (dateMonth == 2) {
	if (dayOfMonth >= 30) {
	  monthHasDay = false;
	}
	else if (dayOfMonth == 28 
		 || (dayOfMonth == 29 && isLeapYear(dateYear))) {
	  monthHasDay = true;
	}
	else {
	  monthHasDay = false;
	}
      }
      else if (dayOfMonth == maxDaysPerMonth && 
	       (dateMonth == 4 || dateMonth == 6
		|| dateMonth == 9 || dateMonth == 11)) {
	monthHasDay = false;
      }

      return monthHasDay;
    }

    protected function isLeapYear(year:Number):Boolean {
      var leapYear:Boolean = false;
      if (year > 0 && year % 4 == 0
	  && (year % 100 != 0 || year % 400 == 0)) {
	leapYear = true;
      }
      return leapYear;
    }

    override public function getComboHeight():Number {
      var currHeight:Number;
      if (_numberHeight > comboHeight) {
	currHeight = _numberHeight;
      }
      else {
	currHeight = comboHeight;
      }

      return currHeight;
    }

	
   protected function getMonthsList():ArrayCollection {
      if (null == monthsList) {

	monthsList = getNumberList(monthsPerYear, monthsInc, 1);
      }
      return monthsList;
    }

    protected function getDaysList():ArrayCollection {
      if (null == daysList) {

	daysList = getNumberList(maxDaysPerMonth, daysInc, 1);
      }
      return daysList;
    }

    protected function getYearsList():ArrayCollection {
      if (null == yearsList) {

	var currYear:int = _currDate.getFullYear();
	if (_firstYear == 0) {
	  firstYear = currYear;
	}

	yearsList = new ArrayCollection();

	for (var year:int = _firstYear;
	     year >= _firstYear - yearsToList; year--) {

	  yearsList.addItem(year);
	  lastYear = year;
	}

      }
      return yearsList;
    }

    /** 
     * The getComboValues method is called by 'get dateValue()' to
     * create a date from the selected values of the monthsCombo,
     * daysCombo, and yearsCombo. If any or all of these ComboBox
     * controls do not have values set, a valid date cannot be
     * created, so null is returned.
     * 
     * 
     * @return Date - The current date as set by the month, day, and year
     * ComboBox controls or null if no valid date is set with the controls. 
     */
    protected function getComboValues():Date {

      var showYears:Boolean = this.getStyle("showYears");
      // Initialize all of our variables to invalid values, so that we know
      // they get set.
      month = -1;
      day = -1;
      year = -1;
      _dateValue = null;


      if (null != monthsCombo && monthsCombo.selectedIndex >= 0) {

	month = monthsCombo.selectedItem as Number;

	month--;
      }

      if (null != daysCombo && daysCombo.selectedIndex >= 0) {

	day = daysCombo.selectedItem as Number;

      }

      if (showYears && null != yearsCombo
	  && yearsCombo.selectedIndex >= 0) {
	year = yearsCombo.selectedItem as Number;

      }

      if (month >= 0 && day >= 0 && year >= 0) {

	_dateValue = new Date(year, month, day);

      }

      //      dumpComboValues("getComboValues", month, day, year);

      return _dateValue;
    }

    /** 
     * The setComboValues method is called by 'set dateValue()', to
     * set the month, day, and year ComboBox controls, if _dateValue
     * is not null.
     * 
     * @return 
     */
    protected function setComboValues():void {

      if (null == _dateValue) {
	clearComboValues();
	return;
      }
      year = -1;
      month = -1;
      day = -1;

      if (null != monthsCombo) {
	month = _dateValue.getMonth();
	month++;
	monthsCombo.selectedItem = month;
      }

      if (null != daysCombo) {
	day = _dateValue.getDate();
	daysCombo.selectedItem = day;

      }


      if (null != yearsCombo) {
	year = _dateValue.getFullYear();

	yearsCombo.selectedItem = year;
      }


    }


    /** 
     * The clearComboValues method is called by setComboValues if
     * _dateValue is null. It sets all of the ComboBox controls to the
     * unselected state.
     * 
     * @return 
     */
    protected function clearComboValues():void {
      var showYears:Boolean = this.getStyle("showYears");

	if (null != monthsCombo) {
	  monthsCombo.selectedIndex = -1;
	}

	if (null != daysCombo) {
	  daysCombo.selectedIndex = -1;
	}
	if (showYears && null != yearsCombo) {
	  yearsCombo.selectedIndex = -1;
	}

    }

    protected function dumpComboValues(funcName:String, 
				       month:Number = -1, day:Number = -1, 
				       year:Number = -1):void {

      trace("Caller = " + funcName
	    + ":" + this.name);

      trace("Month = " + month
	    + " Day = " + day 
	    + " Year = " + year);

      trace("Years.selIdx = "
	    + (null != yearsCombo 
	       ? "SIDX: " + yearsCombo.selectedIndex
	       + " SITM:" + yearsCombo.selectedItem : "NULL"));

      trace("Mons.selIdx = "
	    + (null != monthsCombo 
	       ? "SIDX: " + monthsCombo.selectedIndex
	       + " SITM:" + monthsCombo.selectedItem : "NULL"));

      trace("Days.selIdx = "
	    + (null != daysCombo
	       ? "SIDX: " + daysCombo.selectedIndex 
	       + " SITM:" + daysCombo.selectedItem : "NULL"));

      trace("dateValue = "
	    + (_dateValue != null 
	       ? _dateValue.toString() 
	       + " " + _dateValue.getMonth() 
	       + " " + _dateValue.getDate() 
	       + " " + _dateValue.getFullYear() : "NULL"));

    }

    protected function getDatePartComboWidth(datePartIdx:int):Number {
      var datePartMult:Number = 1;
      var dateDisplayOrder:String = getStyle("dateDisplayOrder");

      if (datePartIdx == 1) {
	if (dateDisplayOrder == DateCombo.YEAR_DAY_MONTH
	    || dateDisplayOrder == DateCombo.YEAR_MONTH_DAY) {
	  datePartMult = 2;
	}
      }

      if (datePartIdx == 3) {
	if (dateDisplayOrder == DateCombo.MONTH_DAY_YEAR
	    || dateDisplayOrder == DateCombo.DAY_MONTH_YEAR) {
	  datePartMult = 2;
	}
      }

      return getComboWidth(datePartMult);

    }

    public function getComboWidth(numWidthMult:Number = 1):Number {
      comboWidth = 0;
      if (_numberWidth == 0) {
	initWidthValues();
      }
      if (downArrowWidth == 0) {
	downArrowWidth = monthsCombo.getStyle("arrowButtonWidth");
      }
      comboWidth = (_numberWidth * numWidthMult)
	+ downArrowWidth + comboWidthPad;

      if (comboWidth < minComboWidth) {
	comboWidth = minComboWidth;
      }
      return comboWidth;

    }


    [Bindable("valueCommit")]
      public function get dateValue():Date {
      _dateValue = getComboValues();
      return _dateValue;
    }

    public function set dateValue(dateValue:Date):void {
      _dateValue = dateValue;
      setComboValues();

      this.invalidateProperties();
      this.dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
    }

    public function get day():Number {
      return _day;
    }

    [Bindable]
    public function set day(day:Number):void {
      _day = day;
    }

    public function get month():Number {
      return _month;
    }

    [Bindable]
    public function set month(month:Number):void {
      _month = month;
    }

    public function get year():Number {
      return _year;
    }

    [Bindable]
    public function set year(year:Number):void {
      _year = year;
    }

    public function get firstYear():int {
      return _firstYear;
    }

    [Bindable]
    public function set firstYear(firstYear:int):void {
      _firstYear = firstYear;
    }

    public function get lastYear():int {
      return _lastYear;
    }

    [Bindable]
      public function set lastYear(lastYear:int):void {
      _lastYear = lastYear;
    }

    public function get yearsToList():int {
      return _yearsToList;
    }

    [Bindable]
      public function set yearsToList(yearsToList:int):void {
      _yearsToList = yearsToList;
    }

  }
}

